﻿<!DOCTYPE html>
<html>
<head>
<title>R</title>
<style>
div {

}
#board tr td{
width: 30px;
height: 30px;
border: 1px;
}
#main{
float: left;
background-color: black;

margin-left: 20px;

}
#preBoard tr td{
width: 30px;
height: 30px;
background-color: black;
}
.button {
background-color: #54CD59;
width:50px;
height:25px;
border-radius: 200px;
margin-left: 15px;
}
#begin{
background-color: #54CD59;
width:100px;
height:50px;
border-radius: 200px;
margin-top: 20px;
margin-left: 20px;
}
</style>
<meta charset="UTF-8"/>
</head>
<script language="javascript">
var TIME = 1000;
var color = "#54CD59";
var preTime;
var mainTb;
//预览窗口
var preTb;
//游戏状态 0: 未开始;1 运行; 2 中止;
var status = 0;
//定时器, 定时器内将做moveDown操作
var timer;
//分数
var score = 0;
//board是一个18*10的数组,也和页面的table对应.
//用来标注那些方格已经被占据. 初始时都为0, 如果被占据则为1
var board = new Array(18);
for(var i=0;i<18;i++){
board[i] = new Array(10);
}
for(var i=0;i<18;i++){
for(var j=0; j<10; j++){
board[i][j] = 0;
}
}
//当前活动的方块, 它可以左右下移动, 变型.当它触底后, 将会更新board;
var activeBlock;
//下一个图形
var nextBlock;
//下一个图形预览
var previewBlock;
//生产方块形状, 有7种基本形状.
function initBlock(){
var block = new Array(4);
//均衡获取0-6之间的随机数
var t = Math.floor(Math.random()*7);
switch(t){
case 0:{
block[0] = {x:0, y:4};
block[1] = {x:1, y:4};
block[2] = {x:0, y:5};
block[3] = {x:1, y:5};
break;
}
case 1:{
block[0] = {x:0, y:3};
block[1] = {x:0, y:4};
block[2] = {x:0, y:5};
block[3] = {x:0, y:6};
break;
}
case 2:{
block[0] = {x:0, y:5};
block[1] = {x:1, y:4};
block[2] = {x:1, y:5};
block[3] = {x:2, y:4};
break;
}
case 3:{
block[0] = {x:0, y:4};
block[1] = {x:1, y:4};
block[2] = {x:1, y:5};
block[3] = {x:2, y:5};
break;
}
case 4:{
block[0] = {x:0, y:4};
block[1] = {x:1, y:4};
block[2] = {x:1, y:5};
block[3] = {x:1, y:6};
break;
}
case 5:{
block[0] = {x:0, y:4};
block[1] = {x:1, y:4};
block[2] = {x:2, y:4};
block[3] = {x:2, y:5};
break;
}
case 6:{
block[0] = {x:0, y:5};
block[1] = {x:1, y:4};
block[2] = {x:1, y:5};
block[3] = {x:1, y:6};
break;
}
}
return block;
}
//向下移动
function moveDown(){
//检查底边界.
if(checkBorder("down")){
//没有触底, 则擦除当前图形
paint(mainTb,activeBlock,"black");
//更新当前图形坐标
for(var i=0; i<4; i++){
activeBlock[i].x = activeBlock[i].x + 1;
}
//重画当前图形
paint(mainTb,activeBlock,color);
}
//触底,
else{
//停止当前的定时器, 也就是停止自动向下移动.
clearInterval(timer);
//标记触底单元格.
updateBoard();
//消行
var lines = deleteLine();
//如果有消行, 则
if(lines!=0){
//更新分数
//一次消多行则分数加倍
if(lines==2){
lines=3;
}
else if(lines==3){
lines=6;
}
else if(lines==4){
lines=10;
}
score = score + lines;
updateScore();
//擦除整个面板
eraseBoard();
//重绘面板
paintBoard();
}
//erasePreview();
paint(preTb,previewBlock,"black");
//产生一个新图形并判断是否可以放在最初的位置.
if(!validateBlock(nextBlock)){
alert("Game over!");
status = 2;
return;
};
activeBlock = nextBlock;
nextBlock = initBlock();
previewBlock = copyBlock(nextBlock);
paint(mainTb,activeBlock,color);
//定时器, 每隔一秒执行一次moveDown
applyPreview();
paint(preTb,previewBlock,color);
timer = setInterval(moveDown,TIME);
}
}
function validateBlock(block){
if(!block){
return false;
}
for(var i=0; i<4; i++){
if(!isCellValid(block[i].x, block[i].y)){
return false;
}
}
return true;
}

function move(direction){
if(checkBorder(direction)){
paint(mainTb,activeBlock,"black");
for(var i=0; i<4; i++){
if(direction == "left"){
activeBlock[i].y = activeBlock[i].y - 1;
}else if(direction == "right"){
activeBlock[i].y = activeBlock[i].y + 1;
}
}
paint(mainTb,activeBlock,color);
}
}
//旋转, 因为旋转之后可能会有方格覆盖已有的方格.
//先用一个tmpBlock,把activeBlock的内容都拷贝到tmpBlock,
//对tmpBlock尝试旋转, 如果旋转后检测发现没有方格产生冲突,则
//把旋转后的tmpBlock的值给activeBlock.
function rotate(){
var tmpBlock = copyBlock(activeBlock);
//先算四个点的中心点，则这四个点围绕中心旋转90度。
var cx = Math.round((tmpBlock[0].x + tmpBlock[1].x + tmpBlock[2].x + tmpBlock[3].x)/4);
var cy = Math.round((tmpBlock[0].y + tmpBlock[1].y + tmpBlock[2].y + tmpBlock[3].y)/4);
//旋转的主要算法. 可以这样分解来理解。
//先假设围绕源点旋转。然后再加上中心点的坐标。
for(var i=0; i<4; i++){
tmpBlock[i].x = cx+cy-activeBlock[i].y;//逆时针旋转90度
tmpBlock[i].y = cy-cx+activeBlock[i].x;
}
//检查旋转后方格是否合法.
for(var i=0; i<4; i++){
if(!isCellValid(tmpBlock[i].x,tmpBlock[i].y)){
return;
}
}
//如果合法, 擦除
paint(mainTb,activeBlock,"black");
//对activeBlock重新赋值.
for(var i=0; i<4; i++){
activeBlock[i].x = tmpBlock[i].x;
activeBlock[i].y = tmpBlock[i].y;
}
//重画.
paint(mainTb,tmpBlock,color);
}
function checkBorder(direction){
for(var i=0; i<activeBlock.length; i++){
if(direction == "left"){
var flag = isCellValid(activeBlock[i].x, activeBlock[i].y-1);
}else if(direction == "right"){
var flag = isCellValid(activeBlock[i].x+1, activeBlock[i].y+1);
}else if(direction == "down"){
var flag = isCellValid(activeBlock[i].x+1, activeBlock[i].y);
}
if(!flag){
return false;
}
}
return true;
}
//检查坐标为(x,y)的是否在board种已经存在, 存在说明这个方格不合法.
function isCellValid(x, y){
if(x>17||x<0||y>9||y<0){
return false;
}
if(board[x][y]==1){
return false;
}
return true;
}

function paint(el,shap,color){
//绿色为活动图形，黑色为擦除的图形
for(var i=0; i<4; i++){
el.rows[shap[i].x].cells[shap[i].y].style.backgroundColor= color;
}
}
//更新board数组
function updateBoard(){
for(var i=0; i<4; i++){
board[activeBlock[i].x][activeBlock[i].y]=1;
}
}
//消行
function deleteLine(){
var lines = 0;
for(var i=0; i<18; i++){
var j=0;
//检测满行
for(; j<10; j++){
if(board[i][j]==0){
break;
}
}
if(j==10){
lines++;
if(i!=0){
for(var k=i-1; k>=0; k--){
//上一行赋值给下一行
board[k+1] = board[k];
}
}
board[0] = generateBlankLine();
}
}
return lines;
}
//擦除整个面板
function eraseBoard(){
for(var i=0; i<18; i++){
for(var j=0; j<10; j++){
mainTb.rows[i].cells[j].style.backgroundColor = "black";
}
}
}
//重绘整个面板
function paintBoard(){
for(var i=0;i<18;i++){
for(var j=0; j<10; j++){
if(board[i][j]==1){
mainTb.rows[i].cells[j].style.backgroundColor = color;
}
}
}
}
//产生一个空白行.
function generateBlankLine(){
var line = new Array(10);
for(var i=0; i<10; i++){
line[i] = 0;
}
return line;
}
//更新分数
function updateScore(){
document.getElementById("score").innerText=" " + score;
}
//键盘控制
function keyControl(){
if(status!=1){
return;
}
var code = event.keyCode;
switch(code){
case 37:{
move("left");
break;
}
case 38:{
rotate();
break;
}
case 39:{
move("right");
break;
}
case 40:{
moveDown();
break;
}
}
}
//辅助函数，拷贝一个图形。
function copyBlock(old){
var o = new Array(4);
for(var i=0; i<4; i++){
o[i] = {x:0, y:0};
}
for(var i=0; i<4; i++){
o[i].x = old[i].x;
o[i].y = old[i].y;
}
return o;
}
//调整previewBlock的坐标以适应预览窗口
function applyPreview(){
var t = 100;
for(var i=0; i<4; i++){
if(previewBlock[i].y<t){
t = previewBlock[i].y;
}
}
//原图形是18X10，预览是4X4,需要在列上左移一个最小单位
for(var i=0; i<4; i++){
previewBlock[i].y-=t;
}

}
function init(){
activeBlock = initBlock();
nextBlock = initBlock();
previewBlock = copyBlock(nextBlock);
paint(mainTb,activeBlock,color);
applyPreview();
paint(preTb,previewBlock,color);
timer = setInterval(moveDown,TIME);
}
//开始
function begin(e){
e.disabled = true;
status = 1;
mainTb = document.getElementById("board");
preTb = document.getElementById("preBoard");
init();
}
document.onkeydown=keyControl;
function hard(){
TIME = 200;
document.getElementById("hard").disabled = true;
}
function easy(){
TIME = 1000;
document.getElementById("easy").disabled = true;
}
function middle(){
TIME = 500;
document.getElementById("middle").disabled = true;
}
</script>
<body>
<Input type="button" value="难" id="hard" class="button" onclick="hard();"/>
<Input type="button" value="中" id="middle" class="button" onclick="middle();"/>
<Input type="button" value="易" id="easy" class="button" onclick="easy();"/>
Score: <span id="score"> 0</span> <br><br>
<div id="main">
<table id="board" cellspacing=0 cellpadding=0 border=1 style="border-collapse:collapse;">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<div style="float: left; width: 5px;">
</div>
<div id="pre">
<table id="preBoard" cellspacing=0 cellpadding=0 border=1 style="border-collapse:collapse;">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
<Input type="button" value="Ready Go" id="begin" onclick="begin(this);"/>
</body>
</html>